package com.specdot.kraster.math;

public class Matrix4x4 {
	public float c00, c01, c02, c03,
				 c10, c11, c12, c13,
				 c20, c21, c22, c23,
				 c30, c31, c32, c33;
	
	public Matrix4x4 mutiply(Matrix4x4 rhs) {
		Matrix4x4 result = new Matrix4x4();
		
		result.c00 = c00 * rhs.c00 + c01 * rhs.c10 + c02 * rhs.c20 + c03 * rhs.c30;
		result.c01 = c00 * rhs.c01 + c01 * rhs.c11 + c02 * rhs.c21 + c03 * rhs.c31;
		result.c02 = c00 * rhs.c02 + c01 * rhs.c12 + c02 * rhs.c22 + c03 * rhs.c32;
		result.c03 = c00 * rhs.c03 + c01 * rhs.c13 + c02 * rhs.c23 + c03 * rhs.c33;
		result.c10 = c10 * rhs.c00 + c11 * rhs.c10 + c12 * rhs.c20 + c13 * rhs.c30;
		result.c11 = c10 * rhs.c01 + c11 * rhs.c11 + c12 * rhs.c21 + c13 * rhs.c31;
		result.c12 = c10 * rhs.c02 + c11 * rhs.c12 + c12 * rhs.c22 + c13 * rhs.c32;
		result.c13 = c10 * rhs.c03 + c11 * rhs.c13 + c12 * rhs.c23 + c13 * rhs.c33;
		result.c20 = c20 * rhs.c00 + c21 * rhs.c10 + c22 * rhs.c20 + c23 * rhs.c30;
		result.c21 = c20 * rhs.c01 + c21 * rhs.c11 + c22 * rhs.c21 + c23 * rhs.c31;
		result.c22 = c20 * rhs.c02 + c21 * rhs.c12 + c22 * rhs.c22 + c23 * rhs.c32;
		result.c23 = c20 * rhs.c03 + c21 * rhs.c13 + c22 * rhs.c23 + c23 * rhs.c33;
		result.c30 = c30 * rhs.c00 + c31 * rhs.c10 + c32 * rhs.c20 + c33 * rhs.c30;
		result.c31 = c30 * rhs.c01 + c31 * rhs.c11 + c32 * rhs.c21 + c33 * rhs.c31;
		result.c32 = c30 * rhs.c02 + c31 * rhs.c12 + c32 * rhs.c22 + c33 * rhs.c32;
		result.c33 = c30 * rhs.c03 + c31 * rhs.c13 + c32 * rhs.c23 + c33 * rhs.c33;
		
		return result;
	}
	
	public Vector4 mutiplyVector4(Vector4 vec) {
		Vector4 result = new Vector4(
				c00 * vec.x + c01 * vec.y + c02 * vec.z + c03 * vec.w,
				c10 * vec.x + c11 * vec.y + c12 * vec.z + c13 * vec.w,
				c20 * vec.x + c21 * vec.y + c22 * vec.z + c23 * vec.w,
				c30 * vec.x + c31 * vec.y + c32 * vec.z + c33 * vec.w
			);
		
		return result;
	}
	
	public void setRow0(float c0, float c1, float c2, float c3) {
		this.c00 = c0;
		this.c01 = c1;
		this.c02 = c2;
		this.c03 = c3;
	}

	public void setRow1(float c0, float c1, float c2, float c3) {
		this.c10 = c0;
		this.c11 = c1;
		this.c12 = c2;
		this.c13 = c3;
	}

	public void setRow2(float c0, float c1, float c2, float c3) {
		this.c20 = c0;
		this.c21 = c1;
		this.c22 = c2;
		this.c23 = c3;
	}

	public void setRow3(float c0, float c1, float c2, float c3) {
		this.c30 = c0;
		this.c31 = c1;
		this.c32 = c2;
		this.c33 = c3;
	}
	
	public static Matrix4x4 indentity(float defaultVal) {
		Matrix4x4 mat = new Matrix4x4();
		mat.c00 = defaultVal;
		mat.c11 = defaultVal;
		mat.c22 = defaultVal;
		mat.c33 = defaultVal;
		return mat;
	}
	
	public static Matrix4x4 indentity() {
		return indentity(1);
	}
	
	public static Matrix4x4 translate(float x, float y, float z) {
		Matrix4x4 mat = indentity();
		mat.c03 = x;
		mat.c13 = y;
		mat.c23 = z;
		return mat;
	}
	
	public static Matrix4x4 rotateX(float rad) {
		Matrix4x4 mat = indentity();
		mat.c11 = (float) Math.cos(rad);
		mat.c12 = (float) -Math.sin(rad);
		mat.c21 = (float) Math.sin(rad);
		mat.c22 = (float) Math.cos(rad);
		return mat;
	}
	
	public static Matrix4x4 rotateY(float rad) {
		Matrix4x4 mat = indentity();
		mat.c00 = (float) Math.cos(rad);
		mat.c02 = (float) Math.sin(rad);
		mat.c20 = (float) -Math.sin(rad);
		mat.c22 = (float) Math.cos(rad);
		return mat;
	}
	
	public static Matrix4x4 rotateZ(float rad) {
		Matrix4x4 mat = indentity();
		mat.c00 = (float) -Math.cos(rad);
		mat.c01 = (float) -Math.sin(rad);
		mat.c10 = (float) Math.sin(rad);
		mat.c11 = (float) Math.cos(rad);
		return mat;
	}
	
	public static Matrix4x4 lookAt(Point3 origin, Dir3 forward, Dir3 up) {
		Dir3 nForward = forward.normalized();
		Dir3 nUp = up.normalized();
		Dir3 nRight = nForward.cross(nUp).normalized();
		
		Matrix4x4 mat = new Matrix4x4();
		mat.setRow0(nRight.x, nRight.y, nRight.z, -origin.dot(nRight));
		mat.setRow0(nUp.x, nUp.y, nUp.z, -origin.dot(nUp));
		mat.setRow0(nForward.x, nForward.y, nForward.z, -origin.dot(nForward));
		mat.c33 = 1;
		
		return mat;
	}
	
	public static Matrix4x4 perspective(float near, float far, float fovY, float aspect) {
		Matrix4x4 result = new Matrix4x4();
		float c = 1f / (float) Math.tan(fovY / 2);
		
		result.c00 = c / aspect;
		result.c11 = c;
		result.c22 = -(far + near) / (far - near);
		result.c23 = -2 * far * near / (far - near);
		result.c32 = -1;
		
		return result;
	}
}
